/*
 *  e100_intr.c
 *  
 *
 *  Created by Yingkai Hu on 11/30/11.
 *  Copyright 2011 NYU. All rights reserved.
 *
 */

#include <xinu.h>

void e100_rxPackets(struct ether *ethptr){
	struct e100_rx_desc *descptr;
	uint32	tail;			/* pos to insert next packet	*/
	uint32	status;			/* status of ring descriptor 	*/
	int numdesc; 			/* num. of descriptor reclaimed	*/
	
	for (numdesc = 0; numdesc < 10; numdesc++) {
		
		/* Insert new arrived packet to the tail */
		
		tail = ethptr->rxTail;
		descptr = (struct e100_rx_desc *)ethptr->rxRing + tail;
		status = descptr->status;
		
		if (!(status & cb_complete)) {
			break;
		}
		
		ethptr->rxTail 
		= (ethptr->rxTail + 1) % ethptr->rxRingSize;
	}
	
	signaln(ethptr->isem, numdesc);
	
	return;
	
}

void 	e100_txPackets(struct	ether *ethptr)		/* ptr to control block		*/						 
{
	struct	e100_tx_desc *descptr;/* ptr to ring descriptor 	*/
	uint32 	head; 			/* pos to reclaim descriptor	*/
	char 	*pktptr; 		/* ptr used during packet copy  */
	int 	numdesc; 		/* num. of descriptor reclaimed	*/
	
	for (numdesc = 0; numdesc < 10; numdesc++) {
		head = ethptr->txHead;
		descptr = (struct e100_tx_desc *)ethptr->txRing + head;
		
		if (!(descptr->status & cb_complete))
			break;
		
		/* Clear the write-back descriptor and buffer */
		
		/*descptr->status = 0;
		descptr->command = 0;
		descptr->link = 0;
		descptr->u.tcb.tbd_array = 0;
		descptr->u.tcb.tcb_byte_count = 0;
		descptr->u.tcb.threshold = 0;
		descptr->u.tcb.tbd_num = 0;*/
		pktptr = descptr->u.tcb.data;
		memset(pktptr, '\0', ETH_BUF_SIZE);
		
		ethptr->txHead 
		= (ethptr->txHead + 1) % ethptr->txRingSize;
	}
	
	signaln(ethptr->osem, numdesc);
	
	return;
}

/*------------------------------------------------------------------------
 * e100_intr - decode and handle interrupt from an E100 device
 *------------------------------------------------------------------------
 */

interrupt e100_intr(
	struct 	ether *ethptr
	)
{
	uint8	stat_ack;
	
	/* Disable device interrupt */
	e100_disable_irq(ethptr);
	
	/* Obtain stat_ack bits from device */
	stat_ack 	= inb(ethptr->iobase + E100_STAT_ACK);
	outb(ethptr->iobase + E100_STAT_ACK, stat_ack);
	
	/* Not our interrupt */
	if (stat_ack == stat_ack_not_ours) {
		e100_enable_irq(ethptr);
		return;
	}
	
	sched_cntl(DEFER_START);
	
	if (stat_ack & stat_ack_rx) {
		ethptr->rxIrq++;
		e100_rxPackets(ethptr);
	}
	
	if (stat_ack & stat_ack_tx) {
		ethptr->txIrq++;
		e100_txPackets(ethptr);
	}
	
	/* Enable device interrupt */

	e100_enable_irq(ethptr);
	
	sched_cntl(DEFER_STOP);

	return;
}
